# -*- coding: utf-8 -*-
from django.http import HttpResponseRedirect
from django.utils.safestring import mark_safe
from django.template.defaultfilters import slugify

from .. import settings, utils
from fields import Field
from callbacks import *

class Sorter(object):
    """
    This object can be used for building sorting options 
    This way the user can easily sort the results displayed 
    in the index page.
    
    Example usage
    ~~~~~~~~~~~~~
    
    **THE VIEW**::
    
        from yafinder import sorters
    
        objects = Article.objects.all()
        
        # sorters
        sorter = sorters.Sorter((
            sorters.Field(label="article name", callback=sorters.from_fields("name")),
            sorters.Field(label="article category", callback=sorters.from_fields("category", "name")),
        ), label="order by", data=request.GET)

        if sorter.is_valid():
            objects = sorter.run(objects)
        else:
            return sorter.redirect()

        # context
        context = {
            "objects": objects,
            "sorter": sorter,
        }
        # output
        return render_to_response(template, context, 
            context_instance=RequestContext(request))
            
    Note that fields is passed to *Sorter* as a sequence.
    A sorter name (the key in querydict) is normally generated slugifying 
    the passed label (that defaults to *order by*), but can be manually 
    overridden passing *name="my-name"* to the sorter constructor.
    
    The **default order** is by the first field in sequence. If you want to 
    change default field all you have to do is pass *is_default=True* to
    the field constructor, e.g.::
        
        sorters.Field(label="category", is_default=True,
            callback=sorters.from_fields("category", "name")),
    
    You can attach **custom callbacks** to fields. A sorter callback takes the 
    same arguments of a filter callback, and obviously must return 
    the manipulated queryset.
        
    The distribution comes with **predefined callback factories**:
        - *yafinder.sorters.from_fields* that takes as *args field names
          that are just passed as *queryset.order_by()* args.
          
    The **order direction** is ascending by default, but you can override this
    passing *desc=True* to the field constructor.
            
    **THE TEMPLATE**
        
    You can customize page title::
        
        Article Index {{ sorter.title }} - 
        
    You can show sorting options using *as_table* (that produces a table row)::
    
        {% if objects.count %}
            <table>{{ sorter.as_table }}</table>
        {% else %}
            No results.
        {% endif %}
    
    or iterating over sorter object::
    
        {% if objects.count %}
            {% for field in sorter %}
                {{ field }}
            {% endfor %}
        {% else %}
            No results.
        {% endif %}
        
    The **title** of the page that can be customized subclassing 
    *Sorter* and overriding the *get_field_title(field)* method, or just 
    defining *YAFINDER_TITLE_PATTERN* in your settings file 
    (defaults to " - %(label)s: %(value_display)s").
    """
    def __init__(self, fields, label=settings.SORTER_LABEL,
        name=None, data=None):
        """
        The fields here are passed as a sequence.
        Takes all the other *args and **kwargs accepted by 
        *django.forms.Form* class.
        """
        self.data = {} if data is None else data
        self.label = label
        self.name = slugify(self.label) if name is None else name
        self._field_list = fields
        self.fields = {}
        
        default_field = None
        
        for field in self._field_list:
            if field.is_default:
                default_field = field
            field.set_querydict(self.data)
            field.name = self.name
            self.fields[field.value] = field
            
        if default_field is None:
            default_field = self._field_list[0]
            default_field.is_default = True
            
        if self.selected_field is None:
            self.selected_field = default_field
            
        self._redirect_url = ""
        
    def selected_field():
        doc = "Return the sorter selected field"
        def fget(self):
            for field in self._field_list:
                if field.is_selected:
                    return field
            return None
        def fset(self, field_to_select):
            for field in self._field_list:
                field.is_selected = False
            if field_to_select:
                field_to_select.is_selected = True
        return locals()
    selected_field = property(**selected_field())
        
    def __iter__(self):
        return iter(self._field_list)
        
    def is_valid(self):
        """
        Return *False* if the querydict value for sorting refers to an invalid
        field. Also set the selected field used for sorting.
        """
        value = self.data.get(self.name)
        if value is not None:
            selected_field = self.fields.get(value.lstrip("-"))
            if selected_field is None:
                self._redirect_url = utils.url_without_key(self.name, self.data)
                return False
            selected_field.is_reversed = value.startswith("-")
            self.selected_field = selected_field
        return True
                            
    def redirect(self):
        """
        Return a redirect response to the cleaned url.
        """
        return HttpResponseRedirect(self._redirect_url)
        
    def run(self, queryset):
        """
        Filter the passed *queryset*.
        The real filtering is done by the callbacks associated to selected
        field.
        """
        return self.selected_field.callback(queryset)
        
    def get_field_title(self, field):
        """
        Override this if you want to customize the field related bit of
        the automatically generated page title.
        """
        reversed_label = u""
        if field.direction == settings.DESC:
            reversed_label = " (%s)" % settings.SORTER_DIRECTION_LABELS[1]
        return u"%s%s" % (field.label, reversed_label)
    
    @property
    def title(self):
        """
        Return an automatically generated page title based on currently
        selected sorting.
        """
        if self.selected_field.is_default:
            return u""
        mapping = {
            "label": self.label, 
            "value_display": self.get_field_title(self.selected_field)
        }
        return settings.TITLE_PATTERN % mapping
        
    def as_table(self):
        """
        Renders the html output of the sorter fields as a table row.
        """
        columns = [u"""<th>%s</th>""" % unicode(i) for i in self._field_list]
        return mark_safe(u"<tr>%s</tr>" % u"".join(columns))
